#include "i2c_hard.h"

#define TIMEOUT 50000

#define I2C_RCU_GPIO_PORN RCU_GPIOB
// 时钟线
#define I2C_SCL_GPIO_PORT GPIOB
#define I2C_SCL_GPIO_PIN GPIO_PIN_6
// 数据线
#define I2C_SDA_GPIO_PORT GPIOB
#define I2C_SDA_GPIO_PIN GPIO_PIN_7

void i2c_hard_config(void)
{
    // 设置引脚复用
    rcu_periph_clock_enable(I2C_RCU_GPIO_PORN);

    // 时钟线引脚:SCL ,复用输出,开漏,浮空
    gpio_mode_set(I2C_SCL_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, I2C_SCL_GPIO_PIN);
    gpio_af_set(I2C_SCL_GPIO_PORT, GPIO_AF_4, I2C_SCL_GPIO_PIN);
    gpio_output_options_set(I2C_SCL_GPIO_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_GPIO_PIN);

    // 数据线引脚:SDA ,复用输出,开漏,浮空
    gpio_mode_set(I2C_SDA_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, I2C_SDA_GPIO_PIN);
    gpio_af_set(I2C_SDA_GPIO_PORT, GPIO_AF_4, I2C_SDA_GPIO_PIN);
    gpio_output_options_set(I2C_SDA_GPIO_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_GPIO_PIN);

    // 重置I2C0
    i2c_deinit(I2C0);
    // 打开I2C时钟
    rcu_periph_clock_enable(RCU_I2C0);
    // 配置I2C时钟,主体配置速度的low/high比例
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
    // 设置模式与地址
    // 当前芯片作为主设备,所以地址设置0x00:如果是从设备,则需要具体的值
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
    // 开启i2c
    i2c_enable(I2C0);
    // ack使能
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
}

/**
 * 1.等待总线空闲
 * 2.发送start信号,判断标志位SBSEND;
 * 3.发送设备地址,判断标志位ADDSEND;清理ADDSEND位;
 * 4.判断TBE,寄存器是否为空,
 */
uint8_t i2c_hard_write(uint8_t addr, uint8_t rega, uint8_t *data, uint32_t len)
{
    uint32_t time_out = 0;

    while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && time_out < TIMEOUT)
    {
        time_out++;
    }

    if (time_out >= TIMEOUT)
        return 1;

    time_out = 0;

    // 发送start信号
    i2c_start_on_bus(I2C0);

    // 判断SBSEND为1
    while (0 == i2c_flag_get(I2C0, I2C_FLAG_SBSEND) && time_out < TIMEOUT)
    {
        time_out++;
    }
    if (time_out >= TIMEOUT)
        return 2;

    time_out = 0;

    // 3.发送设备地址
    i2c_master_addressing(I2C0, addr << 1, I2C_TRANSMITTER);

    // ADDSEND为1,表示地址发送成功,并收到ack响应
    while (0 == i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) && time_out < TIMEOUT)
    {
        time_out++;
    }
    if (time_out >= TIMEOUT)
        return 3;

    time_out = 0;

    // 清除ADDSEND位,需要手动清
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);

    // 发送寄存器地址;
    ////先判断DATA寄存器是否为空,为空才能发送数据,TBE标志
    while (i2c_flag_get(I2C0, I2C_FLAG_TBE) == 0 && time_out < TIMEOUT)
    {
        time_out++;
    }
    if (time_out >= TIMEOUT)
        return 4; // TBE

    time_out = 0;
    // TBE无需清理,向DATA寄存器写数据TBE自动归零(0表示有数据 ,不允许写入)
    // 写寄存器地址
    i2c_data_transmit(I2C0, rega);
    // BTC:字节发送完成,1位发送完成
    // 注意:文档里没有提,但
    // while (i2c_flag_get(I2C0, I2C_FLAG_BTC) == 0 && time_out < TIMEOUT)
    // {
    //     time_out++;
    // }
    // if (time_out >= TIMEOUT)
    //     return 5; // BTC失败
    // time_out = 0;

    // 发送数据
    uint32_t i;
    for (i = 0; i < len; i++)
    {
        // 先判断DATA寄存器是否为空,为空才能发送数据,TBE标志
        while (i2c_flag_get(I2C0, I2C_FLAG_TBE) == 0 && time_out < TIMEOUT)
        {
            time_out++;
        }
        if (time_out >= TIMEOUT)
        {
            return 4;
        }

        time_out = 0;
        // TBE无需清理,向DATA寄存器写数据TBE自动归零(0表示有数据 ,不允许写入)
        // 写寄存器地址
        i2c_data_transmit(I2C0, data[i]);
        // BTC:字节发送完成,1位发送完成
        // 注意:文档里没有提,但
        // while (i2c_flag_get(I2C0, I2C_FLAG_BTC) == 0 && time_out < TIMEOUT)
        // {
        //     time_out++;
        // }
        // if (time_out >= TIMEOUT)
        //     return 5; // BTC失败
        // time_out = 0;
    }

    //
    while (i2c_flag_get(I2C0, I2C_FLAG_BTC) == 0 && time_out < TIMEOUT)
    {
        time_out++;
    }
    if (time_out >= TIMEOUT)
        return 5; // BTC失败
    time_out = 0;

    // 发送stop,产生stop信号
    i2c_stop_on_bus(I2C0);

    // 判断stop信号是否发送成功
    while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && time_out < TIMEOUT)
    {
        time_out++;
    }
    if (time_out >= TIMEOUT)
    {
        return 7; // stop  信号发送失败
    }

    time_out = 0;

    return 0;
}

/**
 * 读
 */
uint8_t i2c_hard_read(uint8_t addr, uint8_t rega, uint8_t *data, uint32_t len)
{
    uint32_t tout = 0;
    uint32_t i;
    /* 1. 先判断总线是否空闲 */
    while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 1; // error，总线忙
    tout = 0;

    /* 2. 发送start 信号 */
    i2c_start_on_bus(I2C0); // 在总线产生Start信号
    // 判断SBSEND为1（start发送）
    while (i2c_flag_get(I2C0, I2C_FLAG_SBSEND) == 0 && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 2; // start信号发送失败
    tout = 0;

    /* 3. 发送设备地址（写） */ // 发送设备地址是向DATA寄存器写入内容被I2C电路发出去，会附带清零SBSEND
    // 发送设备写地址，注意设备地址要<<1 最后一个参数是方向，表明此时是主机发送数据
    i2c_master_addressing(I2C0, addr << 1, I2C_TRANSMITTER);
    // 判断ADDSEND为1（地址发送成功，并收到ACK回复）
    while (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) == 0 && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 3; // 写地址ADDSEND失败
    tout = 0;
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); // ADDSEND需要我们手动clear

    /* 4. 发送寄存器地址 */
    // 先判断DATA寄存器是否为空，这样才能发送数据，TBE标志，为1表示空可发  0表示非空不可发
    while (i2c_flag_get(I2C0, I2C_FLAG_TBE) == 0 && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 4; // TBE失败
    tout = 0;
    // TBE无需清理，向DATA寄存器内部写一个数据，TBE自动归零（0表示有数据，不允许写入）
    i2c_data_transmit(I2C0, rega);

    /* 5. 发送二次start 信号 */
    i2c_start_on_bus(I2C0); // 在总线产生Start信号
    // 判断SBSEND为1（start发送）
    while (i2c_flag_get(I2C0, I2C_FLAG_SBSEND) == 0 && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 5; // 二次start信号发送失败
    tout = 0;

    /* 6. 发送设备地址（读） */ // 发送设备地址是向DATA寄存器写入内容被I2C电路发出去，会附带清零SBSEND
    // 发送设备读地址，方向是I2C_RECEIVER
    i2c_master_addressing(I2C0, (addr << 1) + 1, I2C_RECEIVER);
    // 判断ADDSEND为1（地址发送成功，并收到ACK回复）
    while (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) == 0 && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 6; // 读地址ADDSEND失败
    tout = 0;
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); // ADDSEND需要我们手动clear

    /* 7. 读取数据 */
    // 确保自动ACK功能打开
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
    // 设置ACK的回复模式：对当前数据回复ACK
    // I2C_ACKPOS_CURRENT 收一个数据就立刻回复ACK
    // I2C_ACKPOS_NEXT 收一个数据，不会回复ACK，会对下一个数据回复ACK
    i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);

    for (i = 0; i < len; i++)
    {
        if (i == len - 1)
        {
            // 在最后一个数据接收前，关闭自动ACK，这样就不会回复ACK（不回就是NACK）
            i2c_ack_config(I2C0, I2C_ACK_DISABLE);
        }

        while (i2c_flag_get(I2C0, I2C_FLAG_RBNE) == 0 && tout < TIMEOUT)
        {
            tout++;
        }
        if (tout >= TIMEOUT)
            return 7; // RBNE失败
        tout = 0;

        data[i] = i2c_data_receive(I2C0); // 从数据寄存器中读取数据，放入我们自己的容器内
        // 读取DATA寄存器，会自动将RBNE清零
    }

    /* 8. 发送stop信号 */
    i2c_stop_on_bus(I2C0); // 产生stop信号
    while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && tout < TIMEOUT)
    {
        tout++;
    }
    if (tout >= TIMEOUT)
        return 8; // STOP失败
    tout = 0;

    return 0;
}
